OACでのCloudFrontからS3の接続+Lambda@Edgeでの認証をTerraformで作成してみた
こんにちは、ゲームソリューショングループのsoraです。
今回は、OACでのCloudFrontからS3の接続+Lambda@Edgeでの認証をTerraformで作成してみたことについて書いていきます。
構成
CloudFrontでアクセスを受けると、Lambda@Edgeで認証して、認証が通ればS3にアクセスできるという構成です。
フォルダ構成は以下です。
$ tree . ├── front │ └── front.html ├── lambda │ └── lambda.py └── main.tf
AWSサービスの作成
タイトルにある通り、Terraformを使ってAWS側で必要なサービスを作成します。
解説もコード内のコメントにある程度は記載しています。
特にLambda@Edgeを使用できるリージョンが決まっていること(12-13行目)と、S3に配置するHTMLファイルのcontent-typeを指定すること(25-26行目)に注意してください。
terraform { #AWSプロバイダーのバージョン指定 required_providers { aws = { source = "hashicorp/aws" version = "~> 4.51.0" } } } #AWSプロバイダーの定義 provider aws { #Lambda@Edgeがバージニアリージョンでのみ使用可能なため region = "us-east-1" } #S3 resource aws_s3_bucket origin_contents { bucket = "cloudfront-origin-contents" } #ファイルアップロード resource aws_s3_object object { bucket = aws_s3_bucket.origin_contents.id key = "index.html" source = "front/front.html" #content-typeを指定しない場合、ページが表示されずにダウンロードになる場合があるため指定する content_type = "text/html" } #Lambda用IAMロールの信頼関係の定義 data aws_iam_policy_document assume_role { statement { effect = "Allow" principals { type = "Service" identifiers = [ "lambda.amazonaws.com", "edgelambda.amazonaws.com" ] } actions = ["sts:AssumeRole"] } } #Lambda用IAMロールの作成 resource aws_iam_role iam_for_lambda { name = "cloudfront_access_lambda" assume_role_policy = data.aws_iam_policy_document.assume_role.json inline_policy { name = "my_inline_policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "lambda:InvokeFunction", "lambda:GetFunction", "lambda:EnableReplication", "cloudfront:UpdateDistribution" ] Effect = "Allow" Resource = "*" }, ] }) } } data archive_file lambda { type = "zip" source_file = "lambda/lambda.py" output_path = "lambda_handler.zip" } resource aws_lambda_function lambda { filename = "lambda_handler.zip" function_name = "IPAuth" role = aws_iam_role.iam_for_lambda.arn handler = "lambda.lambda_handler" source_code_hash = data.archive_file.lambda.output_base64sha256 runtime = "python3.8" } #CloudFrontディストリビューション resource aws_cloudfront_distribution cf_distribution { enabled = true default_root_object = "index.html" #オリジンの設定 origin { domain_name = aws_s3_bucket.origin_contents.bucket_regional_domain_name origin_id = aws_s3_bucket.origin_contents.id origin_access_control_id = aws_cloudfront_origin_access_control.main.id } viewer_certificate { cloudfront_default_certificate = true } #キャッシュの設定 default_cache_behavior { target_origin_id = aws_s3_bucket.origin_contents.id viewer_protocol_policy = "redirect-to-https" cached_methods = ["GET", "HEAD"] allowed_methods = ["GET", "HEAD"] forwarded_values { query_string = false headers = [] cookies { forward = "none" } } #ビューワーリクエストにLambdaを設定する lambda_function_association { event_type = "viewer-request" lambda_arn = aws_lambda_function.lambda.qualified_arn include_body = false } } #国ごとのコンテンツ制限がある場合はここで設定(今回はなし) restrictions { geo_restriction { restriction_type = "none" } } } # OACを作成 resource aws_cloudfront_origin_access_control main { name = "cloudfront-oac" origin_access_control_origin_type = "s3" signing_behavior = "always" signing_protocol = "sigv4" } #S3バケットポリシー(OACのみから許可する) ##ポリシーの定義 data aws_iam_policy_document allow_access_from_cloudfront { statement { principals { type = "Service" identifiers = ["cloudfront.amazonaws.com"] } actions = [ "s3:GetObject" ] resources = [ "${aws_s3_bucket.origin_contents.arn}/*" ] condition { test = "StringEquals" variable = "AWS:SourceArn" values = [aws_cloudfront_distribution.cf_distribution.arn] } } } ##バケットポリシーのアタッチ resource aws_s3_bucket_policy allow_access_from_cloudfront { bucket = aws_s3_bucket.origin_contents.id policy = data.aws_iam_policy_document.allow_access_from_cloudfront.json }
Lambdaで使用するコード(Python)
IPアドレスにより制限をかける関数としています。
今回はGoでなくPythonを使います。
以下のページをとても参考にさせていただきました。
CloudFrontとLambda@Edge ( Python3 )とS3で静的ページにIPアドレス制限とBasic認証を設定する
import base64 #自分のIPアドレス ALLOW_IP = ["X.X.X.X"] ERROR_RESPONSE_AUTH = { 'status': '401', 'statusDescription': 'Unauthorized', 'body': 'Authentication Failed', 'headers': { 'www-authenticate': [ { 'key':'WWW-Authenticate', 'value':'IP address fault' } ] } } def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] client_ip = request['clientIp'] if client_ip in ALLOW_IP: return request else: return ERROR_RESPONSE_AUTH
静的ページとして表示させるHTMLも簡単に作成します。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <h1>sora</h1> <h2>所属</h2> <p>営業統括本部 ゲームソリューショングループ ソリューションアーキテクト</p> <h2>今後ブログにしようと思っていること</h2> <ul> <li>CDK(Python)で出たエラー</li> <li>Goで色々作ってみた成果物</li> <li>構築したことのない構成やサービスをCDKで構築</li> </ul> </body> </html>
デプロイ
作成したTerraformのコードを実行して構築します。
terraform init terraform apply
CloudFrontで提供されているドメイン名を確認して、アクセスするとページが表示されています。
最後に
今回は、OACでのCloudFrontからS3の接続+Lambda@Edgeでの認証をTerraformで作成してみたことを記事にしました。
どなたかの参考になると幸いです。